package com.example.library.utils

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.internal.LinkedTreeMap
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.util.Map

object MyJson {

    private val gson = createMapGson()

    fun <T> toJson(`object`: T): String? {
        return try {
            gson.toJson(`object`)
        } catch (ex: Exception) {
            ex.printStackTrace()
            null
        }
    }

    fun fromMap(json: String): Map<String, Any>? {
        return try {
            gson.fromJson(json, object : TypeToken<Map<String, Any>>() {}.type)
        } catch (ex: Exception) {
            ex.printStackTrace()
            null
        }
    }

    fun <T> fromJson(json: String, classOfT: Class<T>): T? {
        return try {
            gson.fromJson(json, classOfT)
        } catch (ex: Exception) {
            ex.printStackTrace()
            null
        }
    }

    fun <T> fromJson(json: String, typeOfT: Type): T? {
        return try {
            gson.fromJson(json, typeOfT)
        } catch (ex: Exception) {
            ex.printStackTrace()
            null
        }
    }

    private fun createMapGson(): Gson {
        val gsonBuilder = GsonBuilder()
        gsonBuilder.registerTypeAdapter(object : TypeToken<Map<String, Any>>() {}.type, MapDeserializerDoubleAsIntFix())
        return gsonBuilder.create()
    }

    private class MapDeserializerDoubleAsIntFix : JsonDeserializer<Any> {

        override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Any? {
            return read(json)
        }

        private fun read(`in`: JsonElement): Any? {
            return when {
                `in`.isJsonArray -> {
                    val list: MutableList<Any> = ArrayList()
                    val arr = `in`.asJsonArray
                    for (i in 0 until arr.size()) {
                        read(arr[i])?.let { list.add(it) }
                    }
                    list
                }
                `in`.isJsonObject -> {
                    val map: MutableMap<String, Any> = LinkedTreeMap()
                    val obj = `in`.asJsonObject
                    val entitySet: MutableSet<MutableMap.MutableEntry<String, JsonElement>>? = obj.entrySet()
                    for ((key, value) in entitySet!!) {
                        map[key] = read(value) as Any
                    }
                    map
                }
                `in`.isJsonPrimitive -> {
                    val prim = `in`.asJsonPrimitive
                    when {
                        prim.isBoolean -> prim.asBoolean
                        prim.isString -> prim.asString
                        prim.isNumber -> {
                            val num = prim.asNumber
                            when {
                                Math.ceil(num.toDouble()) == num.toInt().toDouble() -> num.toInt()
                                Math.ceil(num.toDouble()) == num.toLong().toDouble() -> num.toLong()
                                else -> num.toDouble()
                            }
                        }
                        else -> null
                    }
                }
                else -> null
            }
        }
    }
}